#include <QtGlobal>
#include <QtDebug>

#include <QXmlDefaultHandler>
#include <QProcess>

#include "filedispatcher.h"
#include "localxmlconfig.h"
#include "remotexmlconfig.h"

FileDispatcher::FileDispatcher()
{
	new_file = true;
	tmp_dir = 0;
	connect(&ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool)));
	connect(&ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(ftpCommandFinished(int, bool)));
	connect(&ftp, SIGNAL(dataTransferProgress(qint64, qint64)), this, SLOT(ftpDataTransferProgress(qint64, qint64)));
	connect(&ftp, SIGNAL(stateChanged(int)), this, SLOT(ftpStateChanged(int)));
	connect(&timer, SIGNAL(timeout()), this, SLOT(connectionTimeout()));
}

void FileDispatcher::setURL(const QString &u)
{
	str_url = u;
	url = str_url;
	if (!url.isValid())
	{
                throw(tr("В файле-конфигурации неверно задан URL сервера"));
		return;
	}
	if (url.scheme() != "ftp")
	{
                throw(tr("URL должен начинаться с 'ftp:'!"));
		return;
	}
	if (url.path().isEmpty()) ftp_folder = "/"; else ftp_folder = url.path();
}

void FileDispatcher::setLogin(const QString &usr, const QString &psw)
{
	user_name = usr;
	password = psw;
}

void FileDispatcher::setConfigFile(const QString &f)
{
	config_file = f;
	if (config_file.isEmpty())
	{
                throw(tr("Не указано имя файла-конфигурации на стороне сервера"));
	}
}

void FileDispatcher::setTemporaryFolder(const QString &f)
{
	if (f.isEmpty())
	{
		temporary_folder = QDir::currentPath() + "/tmp";
	}
	else if (QDir::isRelativePath(f))
	{
		temporary_folder = QDir::currentPath() + "/" + f;
	}
	else
	{
		temporary_folder = f;
	}
}

void FileDispatcher::setConectionTimeout(int v)
{
	timeout = v;
	timer.setInterval(timeout * 1000);
}

const QUrl &FileDispatcher::getURL()
{
	return url;
}

const QString &FileDispatcher::getUser()
{
	return user_name;
}

const QString &FileDispatcher::getPassword()
{
	return password;
}

const QString &FileDispatcher::getConfigFile()
{
	return config_file;
}

const QString &FileDispatcher::getTemporaryFolder()
{
	return temporary_folder;
}

FTPFile *FileDispatcher::getFileByName(const QString &nme)
{
	FTPFile *f;
	QListIterator<FTPFile *> i(files);
	while (i.hasNext())
	{
		f = i.next();
		if (f->getConfigName() == nme) return f;
	}
	return 0;
}

int FileDispatcher::getConectionTimeout()
{
	return timeout;
}

void FileDispatcher::ftpStateChanged(int state)
{
	switch (state)
	{
		case QFtp::Connecting: timer.start(); break;
		case QFtp::Connected: timer.stop(); break;
	}
}

void FileDispatcher::ftpCommandFinished(int id, bool error)
{
	if (error) return;
	switch (loading_stage)
	{
		case 0: //connecting complite
		{
			loading_stage++;
			ftp.login(getUser(), getPassword());
			break;
		}
		case 1: //login complite
		{
			loading_stage++;
			ftp.get(ftp_folder + getConfigFile(), &ftp_file);
			break;
		}
		case 2: //loading versions.xml complite
		{
			ftp_file.close();
			QXmlSimpleReader reader;
			QString fn = getTemporaryFolder() + "/" + getConfigFile();
			QFile file(fn);
			QXmlInputSource inputSource(&file);
			RemoteXMLConfig handler(this);
			reader.setContentHandler(&handler);
			reader.setErrorHandler(&handler);
			bool ret = reader.parse(&inputSource);
			file.close();
			tmp_dir->remove(fn);
			tmp_dir->rmdir(getTemporaryFolder());
			if (!ret)
			{
                                emit unhandledError(tr("Ошибка разбора удаленного файла-конфигурации"));
				runExecutableFiles();
				emit abnormalExit();
				return;
			}

			loading_stage++;
			current_file = -1;
		}
		case 3: //load next file
		{
			FTPFile *f;
			if (current_file >= 0)
			{
				f = files[current_file];
                                f->setUpdated(true);
                                emit fileLoadingDone(f->getConfigName());
			}
			ftp_file.close();
			while (true)
			{
				current_file++;
				if (current_file >= files.size())
				{
					loading_stage++;
					return;
				}
                                f = files[current_file];
                                if (!f->isUpdated() || !tmp_dir->exists(QDir::currentPath() + "/" + f->getLocalPath())) break;
			}
			tmp_dir->remove(f->getLocalPath());
			ftp_file.setFileName(f->getLocalPath());
                        if(!ftp_file.open(QIODevice::WriteOnly))
			{
                                mkdir(f);
                                ftp_file.open(QIODevice::WriteOnly);
			}
                        ftp.get(ftp_folder + f->getFtpPath(), &ftp_file);
			emit fileLoadingStart(f->getConfigName());
                        new_file = true;
			break;
		}
	}
}

void FileDispatcher::ftpDataTransferProgress(qint64 done, qint64 total)
{
	if (new_file)
	{
		emit fileSizeInit(0, (int)total);
		new_file = false;
	}
	emit fileLoadingProgress(int(done));
}

void FileDispatcher::ftpDone(bool error)
{
	ftp_file.close();
	if (tmp_dir)
	{
		delete tmp_dir;
		tmp_dir = 0;
	}

	if (error)
	{
		emit unhandledError(ftp.errorString());
		runExecutableFiles();
		emit abnormalExit();
	}
	else
	{
		saveConfigFile();
		runExecutableFiles();
		emit allJobsDone();
	}
}

void FileDispatcher::loadingProcess()
{
	QXmlSimpleReader reader;
	QFile file("launcher.xml");
	QXmlInputSource inputSource(&file);
	LocalXMLConfig handler(this);
	reader.setContentHandler(&handler);
	reader.setErrorHandler(&handler);
	bool ret = reader.parse(&inputSource);
	file.close();
        if (!ret)
	{
                throw(tr("Ошибка разбора локального файла-конфигурации."));
        }

	tmp_dir = new QDir(getTemporaryFolder());
	if (!tmp_dir->exists())
	{
		if (!tmp_dir->mkpath(getTemporaryFolder()))
		{
                        throw(tr("Не могу создать временный каталог."));
		}
	}

	ftp_file.setFileName(getTemporaryFolder() + "/" + getConfigFile());
	if (!ftp_file.open(QIODevice::WriteOnly))
	{
                throw(tr("Не могу создать временный файл."));
	}

	loading_stage = 0;
	ftp.connectToHost(url.host(), url.port(21));
}

void FileDispatcher::runExecutableFiles()
{
	QString cmd;
	FTPFile *f;
	QListIterator<FTPFile *> i(files);
	while (i.hasNext())
	{
		f = i.next();
		cmd = f->getExecuteCommand();
		if (!cmd.isEmpty())
		{
                        if (!QProcess::startDetached(cmd))
			{
                                emit unhandledError(tr("Ошибка при попытке запуска основной программы."));
                        }
		}
	}
}

void FileDispatcher::saveConfigFile()
{
	QFile file("launcher.xml");

	if (!file.open(QIODevice::WriteOnly))
	{
		return;
	}
	QTextStream out(&file);
	out.setCodec("UTF-8");
	out << "<?xml version = '1.0' encoding = 'UTF-8'?>" << endl;
	out << "<!DOCTYPE eDocument>" << endl;
	out << "<root>" << endl;
	out << "\t<ftp_server\turl=\"" << str_url << "\"" << endl;
	out << "\t\t\t\tconfig_file=\"" << config_file << "\"" << endl;
	out << "\t\t\t\tuser=\"" << user_name << "\"" << endl;
	out << "\t\t\t\tpassword=\"" << password << "\"" << endl;
	out << "\t\t\t\ttemporary_folder=\"" << temporary_folder << "\"" << endl;
	out << "\t\t\t\ttimeout=\"" << timeout << "\">";

	QString cmd;
	FTPFile *f;
	QListIterator<FTPFile *> i(files);
	while (i.hasNext())
	{
		f = i.next();
		out << endl << "\t\t<file\t";
		out << "config_name=\"" << f->getConfigName() << "\" ";
		out << "local_path=\"" << f->getLocalPath() << "\" ";
		out << "version=\"" << f->getNewVersion() << "\"";
		cmd = f->getExecuteCommand();
		if (!cmd.isEmpty()) out << " execute_command=\"" << cmd << "\"";
                out << "/>";
	}

	out << endl << "\t</ftp_server>" << endl;
	out << "</root>" << endl;
        file.close();
}

void FileDispatcher::connectionTimeout()
{
	timer.stop();
	ftp_file.close();
	if (tmp_dir)
	{
		delete tmp_dir;
		tmp_dir = 0;
	}
        emit unhandledError(tr("Невозможно подключиться к FTP-серверу."));
	runExecutableFiles();
	emit abnormalExit();
}

bool FileDispatcher::mkdir(FTPFile *file)
{
    QString curr_path = QDir::currentPath();
    QString path= file->getLocalPath(), splitter;
    QRegExp rx(QObject::tr("(.)?%1").arg(file->getConfigName()));
    int pos=0;
    while((pos=rx.indexIn(path,pos))!=-1)
    {
        splitter=rx.cap(1);
        pos+=rx.matchedLength();
    }
    path=path.replace(rx,"");
    if(path.isEmpty())
    {
        QDir::setCurrent(curr_path);
        return false;
    }
    QStringList dirs = path.split(splitter,QString::SkipEmptyParts);
    QDir curr_dir;
    for(int i=0;i<dirs.count();i++)
    {
        if(!curr_dir.mkdir(dirs.at(i)))
        {
            QDir::setCurrent(curr_path);
            return false;
        }
        curr_dir.setCurrent(dirs.at(i));
    }
    QDir::setCurrent(curr_path);
    return true;
}
